package com.soecode.wxtools.bean;

import com.soecode.wxtools.api.WxConfig;
import com.soecode.wxtools.exception.AesException;
import com.soecode.wxtools.util.crypto.WXBizMsgCrypt;
import com.soecode.wxtools.util.xml.XStreamCDataConverter;
import com.soecode.wxtools.util.xml.XStreamTransformer;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import lombok.Data;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;


@Data
@XStreamAlias("xml")
public class WxXmlMessage {

    @XStreamAlias("ToUserName")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String toUserName;

    @XStreamAlias("FromUserName")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String fromUserName;

    @XStreamAlias("CreateTime")
    private Long createTime;

    @XStreamAlias("MsgType")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String msgType;

    @XStreamAlias("Content")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String content;

    @XStreamAlias("MsgId")
    private Long msgId;

    @XStreamAlias("PicUrl")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String picUrl;

    @XStreamAlias("MediaId")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String mediaId;

    @XStreamAlias("Format")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String format;

    @XStreamAlias("ThumbMediaId")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String thumbMediaId;

    @XStreamAlias("Location_X")
    private Double locationX;

    @XStreamAlias("Location_Y")
    private Double locationY;

    @XStreamAlias("Scale")
    private Double scale;

    @XStreamAlias("Label")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String label;

    @XStreamAlias("Title")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String title;

    @XStreamAlias("Description")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String description;

    @XStreamAlias("Url")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String url;

    @XStreamAlias("Event")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String event;

    @XStreamAlias("EventKey")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String eventKey;

    @XStreamAlias("Ticket")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String ticket;

    @XStreamAlias("Latitude")
    private Double latitude;

    @XStreamAlias("Longitude")
    private Double longitude;

    @XStreamAlias("Precision")
    private Double precision;

    @XStreamAlias("Recognition")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String recognition;

    @XStreamAlias("ExpiredTime")
    private String expiredTime;

    @XStreamAlias("FailTime")
    private String failTime;

    @XStreamAlias("FailReason")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String failReason;

    @XStreamAlias("TransInfo")
    private WxKf kf;

    @XStreamAlias("KfAccount")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String kfAccount;

    @XStreamAlias("FromKfAccount")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String fromKfAccount;

    @XStreamAlias("ToKfAccount")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String toKfAccount;

    @XStreamAlias("Status")
    @XStreamConverter(value = XStreamCDataConverter.class)
    private String status;
    @XStreamAlias("TotalCount")
    private Integer totalCount;
    @XStreamAlias("FilterCount")
    private Integer filterCount;
    @XStreamAlias("SentCount")
    private Integer sentCount;
    @XStreamAlias("ErrorCount")
    private Integer errorCount;

    @XStreamAlias("ScanCodeInfo")
    private ScanCodeInfo scanCodeInfo = new ScanCodeInfo();

    @XStreamAlias("SendPicsInfo")
    private SendPicsInfo sendPicsInfo = new SendPicsInfo();

    @XStreamAlias("SendLocationInfo")
    private SendLocationInfo sendLocationInfo = new SendLocationInfo();

    private static WxXmlMessage fromXml(String xml) {
        return XStreamTransformer.fromXml(WxXmlMessage.class, xml);
    }

    protected static WxXmlMessage fromXml(InputStream is) {
        return XStreamTransformer.fromXml(WxXmlMessage.class, is);
    }

    private static WxXmlMessage decryptMsg(String encryptedXml,
                                           String timestamp, String nonce, String msgSignature) throws AesException {
        WXBizMsgCrypt pc = new WXBizMsgCrypt(WxConfig.getInstance().getToken(), WxConfig.getInstance().getAesKey(), WxConfig.getInstance().getAppId());
        String plainText = pc.decryptMsg(msgSignature, timestamp, nonce, encryptedXml);
        return fromXml(plainText);
    }

    public static WxXmlMessage decryptMsg(InputStream is, WxConfig wxConfig, String timestamp,
                                          String nonce, String msgSignature) throws AesException {
        try {
            return decryptMsg(IOUtils.toString(is, "UTF-8"), timestamp, nonce, msgSignature);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Data
    private static class WxKf {
        @XStreamAlias("KfAccount")
        @XStreamConverter(value = XStreamCDataConverter.class)
        private String kfAccount;

    }

    @Data
    @XStreamAlias("ScanCodeInfo")
    public static class ScanCodeInfo {

        @XStreamAlias("ScanType")
        @XStreamConverter(value = XStreamCDataConverter.class)
        private String scanType;

        @XStreamAlias("ScanResult")
        @XStreamConverter(value = XStreamCDataConverter.class)
        private String scanResult;
    }

    @Data
    @XStreamAlias("SendPicsInfo")
    public static class SendPicsInfo {

        @XStreamAlias("Count")
        private Long count;

        @XStreamAlias("PicList")
        protected final List<Item> picList = new ArrayList<Item>();


        @Data
        @XStreamAlias("item")
        public static class Item {

            @XStreamAlias("PicMd5Sum")
            @XStreamConverter(value = XStreamCDataConverter.class)
            private String PicMd5Sum;

        }
    }

    @Data
    @XStreamAlias("SendLocationInfo")
    public static class SendLocationInfo {

        @XStreamAlias("Location_X")
        @XStreamConverter(value = XStreamCDataConverter.class)
        private String locationX;

        @XStreamAlias("Location_Y")
        @XStreamConverter(value = XStreamCDataConverter.class)
        private String locationY;

        @XStreamAlias("Scale")
        @XStreamConverter(value = XStreamCDataConverter.class)
        private String scale;

        @XStreamAlias("Label")
        @XStreamConverter(value = XStreamCDataConverter.class)
        private String label;

        @XStreamAlias("Poiname")
        @XStreamConverter(value = XStreamCDataConverter.class)
        private String poiname;
    }
}
